package xj.model.uml;

import static xj.model.uml.UMLModelConstants.ROLE_CLASS;
import static xj.model.uml.UMLModelConstants.ROLE_DATATYPE;
import static xj.model.uml.UMLModelConstants.ROLE_PACKAGE;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import xj.meta.ModelElement;

public class ClassDiagram extends UMLDiagram {

  public ClassDiagram() {
    setType(DiagramType.Class_Diagram);
  }

  public ClassDiagram(String name) {
    super(name);
    setType(DiagramType.Class_Diagram);
  }

  @Override
  public boolean checkAllowedEdge(ModelElement m) {
    if (m != null &&
	m instanceof UMLAssociation) { 
      return true;
    }       
    return false; 
  }

  @Override
  public boolean checkAllowedNode(ModelElement m) { 
    if (m != null && 
	(m instanceof Classifier ||
	 m instanceof DataType ||
	 //m instanceof UMLInterface || 
	 m instanceof UMLPackage ||
	 m instanceof Note)) { 
      return true;
    }
    return false; 
  }

  @Override
  public boolean checkAllowSubnode(ModelElement parentModel, 
				    ModelElement nodeModel) {
    if (parentModel instanceof UMLPackage) { 
      return (nodeModel instanceof UMLPackage || 
	      nodeModel instanceof DataType || 
	      nodeModel instanceof UMLClass); 
    }
    return false; 
  }

  public void addUMLPackage(UMLPackage pkg) {
    if (pkg != null) {
      packages.add(pkg);
      addOwnedElement(pkg, ROLE_PACKAGE);
    }
  }

  public void removeUMLPackage(UMLPackage pkg) {
    if (pkg != null) {
      packages.remove(pkg);
      removeOwnedElement(pkg);
    }
  }

  public Set<UMLPackage> getUMLPackages() {
    return packages;
  }

  public void addUMLClass(UMLClass cls) {
    if (cls != null) {
      classes.add(cls);
      addOwnedElement(cls, ROLE_CLASS);
    }
  }

  public void removeUMLClass(UMLClass cls) {
    if (cls != null) {
      classes.remove(cls);
      removeOwnedElement(cls);
    }
  }

  public Set<UMLClass> getClasses() {
    return classes;
  }

  public void addDataType(DataType d) {
    if (d != null) {
      datatypes.add(d);
      addOwnedElement(d, ROLE_DATATYPE);
    }
  }

  public void removeDataType(DataType d) {
    if (d != null) {
      datatypes.remove(d);
      removeOwnedElement(d);
    }
  }

  public Set<DataType> getDataTypes() {
    return datatypes;
  }

  public UMLClass findClassByName(String name) {
    if (name != null) {
      Iterator<UMLClass> iter = classes.iterator();
      while (iter.hasNext()) {
	UMLClass c = iter.next();
	if (name.equals(c.getName())) {
	  return c;
	}
      }
    }
    return null;
  }

  /**
   * Return the set of associations in this class diagram that connect to
   * the the specified class at either end
   */
  public Set<UMLAssociation> findAssociations(UMLClass c) {
    if (c != null && 
	associations != null && 
	associations.size() > 0) {
      Set<UMLAssociation> result = new HashSet<UMLAssociation>();
      Iterator<UMLAssociation> iter = associations.iterator();
      while (iter.hasNext()) {
	UMLAssociation assoc = iter.next();
	UMLAssociation.AssociationType atype = assoc.getType();
	if (atype == UMLAssociation.AssociationType.Association || 
	    atype == UMLAssociation.AssociationType.Aggregation || 
	    atype == UMLAssociation.AssociationType.Composition) {
	  if (c == assoc.getLeftElement() || 
	      c == assoc.getRightElement()) {
	    result.add(assoc);
	  }
	}
      }
      return result;
    }
    return null;
  }

  /**
   * Return the set of generalizations in this class diagram in which the
   * specified class is a child
   */
  public Set<UMLAssociation> findGeneralizationsAsChild(UMLClass c) {
    if (c != null && associations != null && associations.size() > 0) {
      Set<UMLAssociation> result = new HashSet<UMLAssociation>();
      Iterator<UMLAssociation> iter = associations.iterator();
      while (iter.hasNext()) {
	UMLAssociation assoc = iter.next();
	UMLAssociation.AssociationType atype = assoc.getType();
	if (atype == UMLAssociation.AssociationType.Generalization || 
	    atype == UMLAssociation.AssociationType.Realization) {
	  if (c == assoc.getLeftElement()) {
	    result.add(assoc);
	  }
	}
      }
      return result;
    }
    return null;
  }

  /**
   * Return the set of generalizations in this class diagram in which the
   * specified class is a parent
   */
  public Set<UMLAssociation> findGeneralizationsAsParent(UMLClass c) {
    if (c != null && associations != null && associations.size() > 0) {
      Set<UMLAssociation> result = new HashSet<UMLAssociation>();
      Iterator<UMLAssociation> iter = associations.iterator();
      while (iter.hasNext()) {
	UMLAssociation assoc = iter.next();
	UMLAssociation.AssociationType atype = assoc.getType();
	if (atype == UMLAssociation.AssociationType.Generalization || 
	    atype == UMLAssociation.AssociationType.Realization) {
	  if (c == assoc.getRightElement()) {
	    result.add(assoc);
	  }
	}
      }
      return result;
    }
    return null;
  }

  /**
   * Return the set of all subclasses of the specified class in this class
   * diagram
   */
  public Set<UMLClass> findSubTypes(UMLClass c) {
    if (c != null && associations != null && 
	associations.size() > 0) {
      Set<UMLClass> result = new HashSet<UMLClass>();
      Iterator<UMLAssociation> iter = associations.iterator();
      while (iter.hasNext()) {
	UMLAssociation assoc = iter.next();
	UMLAssociation.AssociationType atype = assoc.getType();
	if (atype == UMLAssociation.AssociationType.Generalization || 
	    atype == UMLAssociation.AssociationType.Realization) {
	  if (c == assoc.getRightElement()) {
	    ModelElement c2 = assoc.getLeftElement();
	    if (c2 instanceof UMLClass) {
	      result.add((UMLClass) c2);
	    }
	  }
	}
      }
      return result;
    }
    return null;
  }

  /**
   * Return the set of all super types of the specified class in this
   * class diagram
   */
  public Set<UMLClass> findSuperTypes(UMLClass c) {
    if (c != null && 
	associations != null && 
	associations.size() > 0) {
      Set<UMLClass> result = new HashSet<UMLClass>();
      Iterator<UMLAssociation> iter = associations.iterator();
      while (iter.hasNext()) {
	UMLAssociation assoc = iter.next();
	UMLAssociation.AssociationType atype = assoc.getType();
	if (atype == UMLAssociation.AssociationType.Generalization || 
	    atype == UMLAssociation.AssociationType.Realization) {
	  if (c == assoc.getLeftElement()) {
	    ModelElement c2 = assoc.getRightElement();
	    if (c2 instanceof UMLClass) {
	      result.add((UMLClass) c2);
	    }
	  }
	}
      }
      return result;
    }
    return null;
  }

  public static Set<String> getClassNameSet(Set<UMLClass> cs) {
    if (cs != null) {
      Set<String> names = new HashSet<String>();
      Iterator<UMLClass> iter = cs.iterator();
      while (iter.hasNext()) {
	UMLClass c = iter.next();
	String name = c.getName();
	if (name != null && 
	    name.length() > 0) {
	  names.add(name);
	}
      }
      return names;
    }
    return null;
  }

  @Override
  public void deconstruct() {
    super.deconstruct();
    classes.clear();
    packages.clear();
  }

  @Override
  public void add(ModelElement parent, ModelElement e, Object arg) {
    if (e != null) {
      if (e instanceof UMLClass) {
	if (parent instanceof UMLPackage) {
	  UMLPackage pkg = (UMLPackage) parent;
	  pkg.addUMLClass((UMLClass) e);
	} else {
	  addUMLClass((UMLClass) e);
	}
      } else if (e instanceof DataType) {
	if (parent instanceof UMLPackage) {
	  UMLPackage pkg = (UMLPackage) parent;
	  pkg.addDataType((DataType) e);
	} else {
	  addDataType((DataType) e);
	}
      } else if (e instanceof UMLPackage) {
	if (parent instanceof UMLPackage) {
	  UMLPackage pkg = (UMLPackage) parent;
	  pkg.addUMLPackage((UMLPackage) e);
	} else {
	  addUMLPackage((UMLPackage) e);
	}
      } else {
	super.add(parent, e, arg);
      }
    }
  }

  @Override
  public String toString() {
    return "ClassDiagrm: name=" + name;
  }

  @Override
  public Object clone() 
    throws CloneNotSupportedException {
    ClassDiagram d = (ClassDiagram) super.clone();

    d.packages = new HashSet<UMLPackage>();
    Iterator<UMLPackage> iter1 = packages.iterator();
    while (iter1.hasNext()) {
      UMLPackage p = iter1.next();
      d.addUMLPackage((UMLPackage) p.clone());
    }

    d.classes = new HashSet<UMLClass>();
    Iterator<UMLClass> iter2 = classes.iterator();
    while (iter2.hasNext()) {
      UMLClass c = iter2.next();
      d.addUMLClass((UMLClass) c.clone());
    }

    d.associations = new HashSet<UMLAssociation>();
    Iterator<UMLAssociation> iter3 = associations.iterator();
    while (iter3.hasNext()) {
      UMLAssociation a = iter3.next();
      d.addUMLAssociation((UMLAssociation) a.clone());
    }

    d.datatypes = new HashSet<DataType>();
    Iterator<DataType> iter4 = datatypes.iterator();
    while (iter4.hasNext()) {
      DataType t = iter4.next();
      d.addDataType((DataType) t.clone());
    }

    return d;
  }

  protected Set<UMLClass> classes = new HashSet<UMLClass>();

  protected Set<DataType> datatypes = new HashSet<DataType>();

  protected Set<UMLPackage> packages = new HashSet<UMLPackage>();

}
